package com.kou.backend.aop;

import cn.hutool.core.util.ObjectUtil;
import com.kou.backend.request.login.UserLoginReqDTO;
import com.kou.backend.resposne.ResultResponse;
import com.kou.backend.util.JsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;

/**
 * 接口请求日志切面
 *
 * @author: Chaojie.Kou
 * @since: 2023/8/31 15:08
 */
@Aspect
@Order(1)
@Component
@Slf4j
public class RequestApiLogAspect {
    /**
     * 登录接口签名
     */
    private static final String LOGIN_SIGNATURE = "ResultResponse com.kou.backend.controller.user.UserLoginController.login(UserLoginReqDTO)";

    /**
     * 接口坏绕切面
     *
     * @param joinPoint 切点
     * @return 接口的继续调用结果
     * @throws Throwable e
     */
    @Around("execution(* com.kou.backend.api..*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("api:{} ---start", joinPoint.getSignature());
        //开始进行接口请求计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        //获取当前请求上下文
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        Object[] args = joinPoint.getArgs();
        String jsonArgs;
        if (LOGIN_SIGNATURE.equals(joinPoint.getSignature().toString())) {
            jsonArgs = hidePassword(args);
        } else {
            jsonArgs = JsonUtils.toJSONString(args);
        }
        //获取地址与请求方法
        String requestUri = request.getRequestURI();
        String requestMethod = request.getMethod();

        log.info("[API Request log][{}:{}] args: {}", requestMethod, requestUri, args);

        Object result = null;
        String jsonResult = "";
        try {
            //接口继续调用结果
            result = joinPoint.proceed(args);
            jsonResult = JsonUtils.toJSONString(result);

            return result;
        } catch (Throwable e) {
            //异常信息打印
            log.error("[API Request with Error Log][{}:{}] requestId: {}, request args: {}, ", requestMethod,
                    requestUri, result == null ? "" : ((ResultResponse<?>) result).getRequestId(), jsonArgs, e);
            throw e;
        } finally {
            //打印接口全信息
            stopWatch.stop();
            log.info(
                    "[API Log Finished Request][{}:{}] requestId: {}, request args: {}, response result: {}, "
                            + "cost time: {}ms",
                    requestMethod, requestUri, result != null ? ((ResultResponse<?>) result).getRequestId() : "",
                    jsonArgs, jsonResult, stopWatch.getTotalTimeMillis());
        }

    }

    /**
     * 日志中需要隐藏用户密码
     */
    private String hidePassword(Object[] args) {
        if (ObjectUtil.isNotEmpty(args)) {
            UserLoginReqDTO loginReqDTO = (UserLoginReqDTO) args[0];
            UserLoginReqDTO logLoginDTO = new UserLoginReqDTO();
            logLoginDTO.setUserAccount(loginReqDTO.getUserAccount());
            return JsonUtils.toJSONString(logLoginDTO);
        }
        return "";
    }
}
